package mvcc

import (
	"errors"
	"sync/atomic"
	"time"
)

func NewSession() *Transaction {
	s := &Transaction{
		TrId:  *TrId,
		level: ReadCommit,
	}
	atomic.AddInt64(TrId, 1)
	return s
}

func (session *Transaction) Begin() error {
	session.Started = true
	return nil
}

func (session *Transaction) RollBack() (err error) {
	defer func() {
		session.Started = false
	}()
	v, ok := LockIdsMap.Load(session.TrId)
	if !ok {
		return
	}
	ids, ok := v.([]int64)
	if !ok {
		return
	}
	err = session.rollback(ids)
	return nil
}

func (session *Transaction) rollback(ids []int64) (err error) {
	defer func() {
		LockIdsMap.Delete(session.TrId)
	}()
	for _, id := range ids {
		rw := GetReadView(id)
		rw.ResetRollPtr(session.TrId, rw.GetData(session.TrId).GetRollPtr())
		rw.RemoveList(session.TrId)
	}

	return
}

func (session *Transaction) Commit() (err error) {
	var changeIds []int64
	defer func() {
		if err == nil {
			//这里应该优化成channel
			LockIdsMap.Delete(session.TrId)
			session.Started = false
		} else {
			err = session.rollback(changeIds)
		}
	}()

	v, ok := LockIdsMap.Load(session.TrId)
	if !ok {
		return
	}
	ids, ok := v.([]int64)
	if !ok {
		return
	}
	for _, id := range ids {
		rw := GetReadView(id)
		row := rw.GetData(session.TrId)
		if rw.CanCommit(id, session.TrId, session.level) {
			Data[id].SetValues(row.GetData())
			Data[id].SetTrId(session.TrId)
			rw.RemoveList(session.TrId)
			rw.ResetRollPtr(session.TrId, row.GetRollPtr())
		} else {
			err = errors.New("tr commit fail")
			break
		}
	}
	return
}

func (session *Transaction) SetLevel(level int) {
	session.level = level
}

func (session *Transaction) sLevel() {
	c := time.After(200 * time.Millisecond)
	for {
		select {
		case <-c:
			panic("tr wait error time out")
			//todo: 其他的事务都执行完了
		}
	}
}
func (session *Transaction) Find(id int64) (IRow, error) {
	if session.level == ReadUnCommit || session.TrId == 0 {
		rw := GetReadView(id)
		l := len(rw.List)
		if l > 0 {
			return rw.List[l-1], nil
		}
		return find(id), nil
	}

	rw := session.sessionGetRw(id)
	return rw.GetData(session.TrId), nil
}

func (session *Transaction) Update(id int64, values map[string]interface{}) {
	if session.level == ReadUnCommit || session.TrId == 0 {
		for k, v := range values {
			Data[id].SetValue(k, v)
			Data[id].SetTrId(session.TrId)
		}
		return
	}
	if v, ok := LockIdsMap.Load(session.TrId); ok {
		ids := v.([]int64)
		if !inArrayIds64(id, ids) {
			LockIdsMap.Store(id, append(ids, id))
		}
	} else {
		LockIdsMap.Store(session.TrId, []int64{id})
	}
	rw := session.sessionGetRw(id)
	row := rw.GetData(session.TrId)
	row.SetValues(values)
}

func (session *Transaction) Delete(id int64) {
	rw := session.sessionGetRw(id)
	row := rw.GetData(session.TrId)
	row.SetValues(nil)
	row.SetRowId(0)
}

func (session *Transaction) Insert(row IRow) {
	session.sessionGetRw(row.GetRowId())
	//插入 范围查询  暂时不在考虑
}

func (session *Transaction) sessionGetRw(id int64) *ReadView {
	rw := GetReadView(id)
	var row IRow
	l := len(rw.List)

	if rw.UpLimitId == 0 || l == 0 {
		rw.UpLimitId = session.TrId //初始化最小活跃id
		h := find(id)
		row = generatorNewRow(h.GetRowId())
		row.SetTrId(session.TrId)
		row.SetRollPtr(h)
		row.SetValues(h.GetData())
		rw.List = append(rw.List, row) //初始版本 第一个事务进入
		return rw
	}
	row = rw.GetData(session.TrId)
	if row != nil && session.level == ReadRepeat {
		// RR级别 如果这个事务存在于RW中，直接返回，解决不可重复读（重复读预期不一致的情况）
		return rw
	}
	if row == nil { //如果当前行不存在readView
		dv := rw.List[l-1] //获取最后一个版本
		row = generatorNewRow(dv.GetRowId())
		row.SetTrId(session.TrId)
		row.SetRollPtr(dv)
		row.SetValues(dv.GetData())
		rw.List = append(rw.List, row)
	} else {
		//RC级别 每次readView都要重设，重新设置数据为最新版本的数据
		row.SetValues(find(id).GetData())
	}

	return rw
}
